<template>
	<div ref="container" class="point-cloud-viewer" @dragover.prevent @drop.prevent="handleFileDrop">
		<!-- 加载进度条 -->
		<div v-if="loading" class="loading-overlay">
			<div class="loading-progress">{{ Math.floor(loadingProgress) }}%</div>
		</div>

		<!-- 控制面板 -->
		<div v-if="showControls" class="control-panel">
			<div class="control-item">
				<label>渲染模式:</label>
				<select v-model="renderMode">
					<option style="background-color: black" value="height">高度渲染</option>
					<option style="background-color: black" value="intensity">强度渲染</option>
					<option style="background-color: black" value="custom">自定义颜色</option>
				</select>
			</div>
			<div class="control-item">
				<label>点大小:</label>
				<input type="range" v-model.number="pointSize" :min="minPointSize" :max="maxPointSize" step="0.1" />
			</div>
			<!-- <div class="control-item">
        <label>坐标轴:</label>
        <input type="checkbox" v-model="showAxesHelper" />
      </div> -->
		</div>
	</div>
</template>

<script lang="ts" setup>
	import * as THREE from "three";
	import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
	import { PCDLoader } from "three/examples/jsm/loaders/PCDLoader";
	import { CSS2DObject, CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer";
	import { onBeforeUnmount, onMounted, ref, watch } from "vue";
	// 类型定义
	type RenderMode = "height" | "intensity" | "custom";
	type ColorMap = "rainbow" | "terrain";

	interface CameraConfig {
		fov: number;
		near: number;
		far: number;
		minDistance: number;
		maxDistance: number;
	}

	interface Props {
		/** 点云文件URL */
		url?: string;
		/** 是否显示控制面板 */
		showControls?: boolean;
		/** 最小点大小 */
		minPointSize?: number;
		/** 最大点大小 */
		maxPointSize?: number;
		/** 默认渲染模式 */
		defaultRenderMode?: RenderMode;
		/** 颜色映射方案 */
		colorMap?: ColorMap;
		/** 相机配置 */
		cameraConfig?: CameraConfig;
		/** 背景色 */
		backgroundColor?: string;
		/** 是否显示坐标轴辅助器 */
		showAxesHelper?: boolean;
		/** 坐标轴辅助器大小 */
		axesHelperSize?: number;
		/** 坐标轴辅助器窗口大小 */
		axesHelperWindowSize?: number;
	}

	// Props定义
	const props = withDefaults(defineProps<Props>(), {
		url: "",
		showControls: true,
		minPointSize: 0.1,
		maxPointSize: 3.0,
		defaultRenderMode: "height",
		colorMap: "rainbow",
		backgroundColor: "#000000",
		showAxesHelper: false,
		axesHelperSize: 30,
		axesHelperWindowSize: 100,
		cameraConfig: () => ({
			fov: 30,
			near: 0.01,
			far: 1000,
			minDistance: 0.1,
			maxDistance: 1000,
		}),
	});

	// Emits定义
	const emit = defineEmits<{
		(e: "load-success"): void;
		(e: "load-error", error: Error): void;
	}>();

	// 颜色工具类
	class ColorUtils {
		static colorMaps = {
			rainbow: [
				[0, 0, 255], // 蓝色
				[0, 255, 255], // 青色
				[0, 255, 0], // 绿色
				[255, 255, 0], // 黄色
				[255, 0, 0], // 红色
			],
			terrain: [
				[0, 102, 0], // 深绿
				[26, 152, 80], // 绿
				[145, 207, 96], // 浅绿
				[217, 239, 139], // 黄绿
				[255, 255, 191], // 浅黄
			],
		} as const;

		static interpolateColor(value: number, min: number, max: number, colorMap: ColorMap): [number, number, number] {
			const rate = (value - min) / (max - min);
			const clampedRate = Math.min(0.99999, Math.max(0, rate));
			const colors = this.colorMaps[colorMap];

			const index = clampedRate * (colors.length - 1);
			const low = Math.floor(index);
			const high = Math.min(colors.length - 1, low + 1);
			const ratio = index - low;

			const color1 = colors[low];
			const color2 = colors[high];

			return [
				(color1[0] * (1 - ratio) + color2[0] * ratio) / 255,
				(color1[1] * (1 - ratio) + color2[1] * ratio) / 255,
				(color1[2] * (1 - ratio) + color2[2] * ratio) / 255,
			];
		}
	}

	// 状态定义
	const container = ref<HTMLDivElement | null>(null);
	const loading = ref(false);
	const loadingProgress = ref(0);
	const renderMode = ref<RenderMode>(props.defaultRenderMode);
	const pointSize = ref(0);
	const showAxesHelper = ref(props.showAxesHelper);
	let labelRenderer: CSS2DRenderer; // 添加新的渲染器变量
	// Three.js 相关变量
	let scene: THREE.Scene;
	let camera: THREE.PerspectiveCamera;
	let renderer: THREE.WebGLRenderer;
	let controls: OrbitControls;
	let points: THREE.Points;
	let axesHelper: THREE.AxesHelper;
	let axesScene: THREE.Scene;
	let axesCamera: THREE.OrthographicCamera;
	let axesRenderer: THREE.WebGLRenderer;
	let animationFrameId: number | null = null;

	// 初始化Three.js场景
	const initScene = () => {
		if (!container.value) return;

		scene = new THREE.Scene();

		camera = new THREE.PerspectiveCamera(
			props.cameraConfig.fov,
			container.value.clientWidth / container.value.clientHeight,
			props.cameraConfig.near,
			props.cameraConfig.far
		);

		renderer = new THREE.WebGLRenderer({ antialias: true });
		renderer.setClearColor(new THREE.Color(props.backgroundColor));
		renderer.setSize(container.value.clientWidth, container.value.clientHeight);
		container.value.appendChild(renderer.domElement);

		controls = new OrbitControls(camera, renderer.domElement);
		controls.minDistance = props.cameraConfig.minDistance;
		controls.maxDistance = props.cameraConfig.maxDistance;

		// 添加光源
		const ambientLight = new THREE.AmbientLight(0x404040);
		const directionalLight = new THREE.DirectionalLight(0xffffff);
		scene.add(ambientLight, directionalLight);
		if (props.showAxesHelper) {
			initAxesHelper();
		}
	};

	// 初始化坐标轴辅助器
	const initAxesHelper = () => {
		if (!container.value) {
			console.warn("Container not found");
			return;
		}

		// 创建带标签的坐标轴辅助器
		axesHelper = createLabeledAxesHelper();

		// 创建独立场景
		axesScene = new THREE.Scene();
		axesScene.add(axesHelper);

		// 调整正交相机的参数
		const size = props.axesHelperWindowSize / 3;
		axesCamera = new THREE.OrthographicCamera(-size, size, size, -size, 0.1, 2000);
		axesCamera.position.set(size * 2, size * 2, size * 2);
		axesCamera.lookAt(0, 0, 0);

		// 创建主渲染器
		axesRenderer = new THREE.WebGLRenderer({
			alpha: true,
			antialias: true,
			preserveDrawingBuffer: true,
		});
		axesRenderer.setSize(props.axesHelperWindowSize, props.axesHelperWindowSize);
		axesRenderer.setClearColor(0x000000, 0.1);

		// 创建标签渲染器
		labelRenderer = new CSS2DRenderer();
		labelRenderer.setSize(props.axesHelperWindowSize, props.axesHelperWindowSize);
		labelRenderer.domElement.style.position = "absolute";
		labelRenderer.domElement.style.pointerEvents = "none";

		// 创建容器div
		const axesContainer = document.createElement("div");
		axesContainer.style.position = "absolute";
		axesContainer.style.bottom = "30px";
		axesContainer.style.right = "30px";
		axesContainer.style.width = `${props.axesHelperWindowSize}px`;
		axesContainer.style.height = `${props.axesHelperWindowSize}px`;
		axesContainer.style.border = "2px solid rgba(255, 255, 255, 0.3)";
		axesContainer.style.borderRadius = "8px";
		axesContainer.style.backgroundColor = "rgba(0, 0, 0, 0.2)";
		axesContainer.style.zIndex = "1000";

		// 添加渲染器到容器
		axesContainer.appendChild(axesRenderer.domElement);
		axesContainer.appendChild(labelRenderer.domElement);

		// 使用正确的container引用
		if (container.value) {
			container.value.appendChild(axesContainer);
		}
	};
	// 移除坐标轴辅助器
	const removeAxesHelper = () => {
		if (axesRenderer) {
			axesRenderer.domElement.parentElement?.remove();
			axesRenderer.dispose();
		}
		if (axesHelper) {
			axesHelper.traverse((object) => {
				if (object instanceof THREE.Mesh) {
					object.geometry.dispose();
					object.material.dispose();
				}
			});
		}
	};

	// 创建带标签的坐标轴辅助器
	const createLabeledAxesHelper = () => {
		const group = new THREE.Group();

		// 创建轴线
		const createAxis = (color: number, direction: THREE.Vector3) => {
			const material = new THREE.LineBasicMaterial({ color });
			const geometry = new THREE.BufferGeometry();
			geometry.setAttribute(
				"position",
				new THREE.Float32BufferAttribute(
					[
						0,
						0,
						0,
						direction.x * props.axesHelperSize,
						direction.y * props.axesHelperSize,
						direction.z * props.axesHelperSize,
					],
					3
				)
			);
			return new THREE.Line(geometry, material);
		};

		// 创建标签球体和文本
		const createLabelWithText = (color: number, position: THREE.Vector3, text: string) => {
			const group = new THREE.Group();

			// 创建球体
			const geometry = new THREE.SphereGeometry(props.axesHelperSize / 15);
			const material = new THREE.MeshBasicMaterial({ color });
			const sphere = new THREE.Mesh(geometry, material);
			sphere.position.copy(position);

			// 创建文本标签
			const textDiv = document.createElement("div");
			textDiv.className = "axis-label";
			textDiv.textContent = text;
			textDiv.style.color = `#${color.toString(16).padStart(6, "0")}`;
			textDiv.style.fontSize = "14px";
			textDiv.style.fontWeight = "bold";
			textDiv.style.userSelect = "none";

			const label = new CSS2DObject(textDiv);
			label.position.copy(position);
			// 稍微偏移文本位置，避免与球体重叠
			label.position.multiplyScalar(1.2);

			group.add(sphere);
			group.add(label);
			return group;
		};

		// X轴（红色）
		const xAxis = createAxis(0xff0000, new THREE.Vector3(1, 0, 0));
		const xLabel = createLabelWithText(0xff0000, new THREE.Vector3(props.axesHelperSize, 0, 0), "X");

		// Y轴（绿色）
		const yAxis = createAxis(0x00ff00, new THREE.Vector3(0, 1, 0));
		const yLabel = createLabelWithText(0x00ff00, new THREE.Vector3(0, props.axesHelperSize, 0), "Y");

		// Z轴（蓝色）
		const zAxis = createAxis(0x0000ff, new THREE.Vector3(0, 0, 1));
		const zLabel = createLabelWithText(0x0000ff, new THREE.Vector3(0, 0, props.axesHelperSize), "Z");

		group.add(xAxis, xLabel, yAxis, yLabel, zAxis, zLabel);
		return group;
	};

	// 加载点云数据
	const loadPointCloud = async (source: string) => {
		if (!container.value) return;

		loading.value = true;
		loadingProgress.value = 0;

		try {
			if (points) scene.remove(points); //移除旧的点云数据
			const loader = new PCDLoader();

			const onProgress = (xhr: ProgressEvent) => {
				loadingProgress.value = (xhr.loaded / xhr.total) * 100;
			};

			const pointCloud = await new Promise<THREE.Points>((resolve, reject) => {
				loader.load(source, resolve, onProgress, reject);
			});

			points = processPointCloud(pointCloud);
			scene.add(points);

			// 立即调整相机位置
			fitCameraToPoints();

			// 确保相机位置更新后再触发成功事件
			renderer.render(scene, camera);
			emit("load-success");
		} catch (error) {
			console.error("Failed to load point cloud:", error);
			emit("load-error", error as Error);
		} finally {
			loading.value = false;
		}
	};

	// 处理点云数据
	const processPointCloud = (pointCloud: THREE.Points) => {
		const geometry = pointCloud.geometry as THREE.BufferGeometry;
		const positions = geometry.attributes.position.array;
		const intensity = geometry.attributes.intensity?.array || new Float32Array(positions.length / 3).fill(1.0);

		const ranges = calculateRanges(positions, intensity);
		const colors = new Float32Array(positions.length);

		for (let i = 0; i < positions.length; i += 3) {
			let color: [number, number, number];

			switch (renderMode.value) {
				case "height":
					color = ColorUtils.interpolateColor(
						positions[i + 2],
						ranges.height.min,
						ranges.height.max,
						props.colorMap
					);
					break;
				case "intensity":
					// 修改这里：使用 i/3 来正确索引强度值
					color = ColorUtils.interpolateColor(
						intensity[i / 3],
						ranges.intensity!.min,
						ranges.intensity!.max,
						props.colorMap
					);
					break;
				case "custom":
				default:
					color = [1, 1, 1];
			}

			colors[i] = color[0];
			colors[i + 1] = color[1];
			colors[i + 2] = color[2];
		}

		const newGeometry = new THREE.BufferGeometry();
		newGeometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
		newGeometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
		// 添加这里：保留原始强度属性
		if (geometry.attributes.intensity) {
			newGeometry.setAttribute("intensity", geometry.attributes.intensity);
		}

		const material = new THREE.PointsMaterial({
			size: pointSize.value,
			vertexColors: true,
			sizeAttenuation: true,
		});

		return new THREE.Points(newGeometry, material);
	};

	// 计算数据范围
	const calculateRanges = (positions: ArrayLike<number>, intensity?: ArrayLike<number>) => {
		const ranges = {
			height: { min: Infinity, max: -Infinity },
			intensity: intensity ? { min: Infinity, max: -Infinity } : null,
		};

		for (let i = 0; i < positions.length; i += 3) {
			ranges.height.min = Math.min(ranges.height.min, positions[i + 2]);
			ranges.height.max = Math.max(ranges.height.max, positions[i + 2]);

			if (intensity) {
				ranges.intensity!.min = Math.min(ranges.intensity!.min, intensity[i / 3]);
				ranges.intensity!.max = Math.max(ranges.intensity!.max, intensity[i / 3]);
			}
		}

		return ranges;
	};

	// 找到最密集的区域
	const findDensestRegion = (points: THREE.Points): THREE.Vector3 => {
		const geometry = points.geometry as THREE.BufferGeometry;
		const positions = geometry.getAttribute("position");

		// 创建网格来统计点的密度
		const gridSize = 50; // 网格划分数量
		const densityGrid: { [key: string]: number } = {};
		let maxDensity = 0;
		let densestCell = new THREE.Vector3();

		// 计算点云的边界盒
		const box = new THREE.Box3().setFromObject(points);
		const size = box.getSize(new THREE.Vector3());
		const min = box.min;

		// 遍历所有点，统计每个网格中的点数
		for (let i = 0; i < positions.count; i++) {
			const x = positions.getX(i);
			const y = positions.getY(i);
			const z = positions.getZ(i);

			// 将点的坐标转换为网格索引
			const gridX = Math.floor(((x - min.x) / size.x) * gridSize);
			const gridY = Math.floor(((y - min.y) / size.y) * gridSize);
			const gridZ = Math.floor(((z - min.z) / size.z) * gridSize);

			const key = `${gridX},${gridY},${gridZ}`;
			densityGrid[key] = (densityGrid[key] || 0) + 1;

			// 更新最大密度
			if (densityGrid[key] > maxDensity) {
				maxDensity = densityGrid[key];
				// 计算网格中心点的实际坐标
				densestCell.set(
					min.x + ((gridX + 0.5) * size.x) / gridSize,
					min.y + ((gridY + 0.5) * size.y) / gridSize,
					min.z + ((gridZ + 0.5) * size.z) / gridSize
				);
			}
		}

		return densestCell;
	};

	// 调整相机视角
	const fitCameraToPoints = () => {
		if (!points) return;

		const box = new THREE.Box3().setFromObject(points);
		const size = box.getSize(new THREE.Vector3());
		const maxDim = Math.max(size.x, size.y, size.z);

		// 获取最密集区域的中心点
		const densestPoint = findDensestRegion(points);

		// 调整相机位置，使其对准最密集区域
		camera.position.set(densestPoint.x, densestPoint.y - maxDim * 0.5, densestPoint.z + maxDim * 0.5);
		camera.lookAt(densestPoint);

		// 更新控制器的目标点为最密集区域
		controls.target.copy(densestPoint);
		controls.update();
	};

	// 动画循环
	// 修改动画循环
	const animate = () => {
		animationFrameId = requestAnimationFrame(animate);
		controls.update();
		renderer.render(scene, camera);

		// 更新坐标轴辅助器
		if (showAxesHelper.value && axesHelper && axesRenderer && axesCamera && labelRenderer) {
			// 同步主相机的旋转
			const quaternion = new THREE.Quaternion();
			camera.getWorldQuaternion(quaternion);
			axesHelper.setRotationFromQuaternion(quaternion);

			// 渲染3D场景和2D标签
			axesRenderer.render(axesScene, axesCamera);
			labelRenderer.render(axesScene, axesCamera);
		}
	};

	// 处理文件拖放
	const handleFileDrop = (event: DragEvent) => {
		const file = event.dataTransfer?.files[0];
		if (file && file.name.toLowerCase().endsWith(".pcd")) {
			const url = URL.createObjectURL(file);
			loadPointCloud(url);
		}
	};

	// 处理窗口大小调整
	const handleResize = () => {
		if (!container.value || !camera || !renderer) return;

		camera.aspect = container.value.clientWidth / container.value.clientHeight;
		camera.updateProjectionMatrix();
		renderer.setSize(container.value.clientWidth, container.value.clientHeight);
	};

	// 生命周期钩子
	onMounted(() => {
		console.log("Component mounted, showAxesHelper:", props.showAxesHelper);
		initScene();
		if (props.showAxesHelper) {
			initAxesHelper();
		}
		if (props.url) {
			loadPointCloud(props.url);
		}
		animate();
		window.addEventListener("resize", handleResize);
	});

	// 在组件卸载时清理
	onBeforeUnmount(() => {
		if (animationFrameId) cancelAnimationFrame(animationFrameId);
		window.removeEventListener("resize", handleResize);

		if (renderer) {
			renderer.dispose();
			const gl = renderer.domElement.getContext("webgl");
			gl?.getExtension("WEBGL_lose_context")?.loseContext();
		}
		if (points) {
			points.geometry.dispose();
			points.material.dispose();
		}
		removeAxesHelper();
	});

	// 监听属性变化
	watch(
		() => props.url,
		(newUrl) => {
			if (newUrl) loadPointCloud(newUrl);
		}
	);

	watch(renderMode, () => {
		if (points) {
			const newPoints = processPointCloud(points);
			scene.remove(points);
			scene.add(newPoints);
			points = newPoints;
		}
	});

	watch(pointSize, (newSize) => {
		if (points) {
			points.material.size = newSize;
		}
	});

	watch(
		() => props.showAxesHelper,
		(newValue) => {
			showAxesHelper.value = newValue;
			if (newValue) {
				initAxesHelper();
			} else {
				removeAxesHelper();
			}
		}
	);

	watch(
		showAxesHelper,
		(newValue) => {
			console.log("showAxesHelper changed:", newValue);
			if (newValue) {
				initAxesHelper();
			} else {
				removeAxesHelper();
			}
		},
		{ immediate: true }
	);

	// 暴露方法
	defineExpose({
		resetCamera: fitCameraToPoints,
		setRenderMode: (mode: RenderMode) => (renderMode.value = mode),
		setPointSize: (size: number) => (pointSize.value = size),
	});
</script>

<style scoped>
	.point-cloud-viewer {
		position: relative;
		width: 100%;
		height: 100%;
		background-color: #000;
	}

	.loading-overlay {
		position: absolute;
		inset: 0;
		display: flex;
		color: white;
		background: rgb(0 0 0 / 50%);
		justify-content: center;
		align-items: center;
	}

	.control-panel {
		position: absolute;
		top: 10px;
		right: 10px;
		padding: 10px;
		color: white;
		background: rgb(0 0 0 / 50%); /* 更低的透明度，增加透明感 */
		border-radius: 4px;
		backdrop-filter: blur(15px) brightness(1.2); /* 增加模糊和亮度 */
	}

	.control-item {
		display: flex;
		margin-bottom: 8px;
		align-items: center;
		gap: 10px;
	}

	.control-item select,
	.control-item input {
		padding: 4px;
		color: white;
		background: rgb(255 255 255 / 10%);
		border: 1px solid rgb(255 255 255 / 30%);
		border-radius: 2px;
	}

	.axis-label {
		padding: 2px 4px;
		pointer-events: none;
		background-color: rgb(0 0 0 / 60%);
		border-radius: 4px;
	}
</style>
